package com.treasure.mall.zbqmallapi.controller;

import com.alibaba.fastjson.JSON;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.treasure.mall.biz.dto.common.ChatMessageBodyDTO;
import com.treasure.mall.biz.dto.condition.LiveHistoryMessageCondition;
import com.treasure.mall.biz.dto.condition.LiveRoomFanCondition;
import com.treasure.mall.biz.dto.condition.LiveRoomSearchCondition;
import com.treasure.mall.biz.entity.live.LiveHistoryMessagePO;
import com.treasure.mall.biz.entity.live.LiveRoomFanPO;
import com.treasure.mall.biz.entity.live.LiveRoomPO;
import com.treasure.mall.biz.entity.live.LiveSessionPO;
import com.treasure.mall.biz.entity.user.UserPO;
import com.treasure.mall.biz.remote.live.TencentIMService;
import com.treasure.mall.biz.remote.live.TencentLiveService;
import com.treasure.mall.biz.remote.live.TencentTools;
import com.treasure.mall.biz.remote.live.dto.JoinMessageDTO;
import com.treasure.mall.biz.remote.live.dto.LiveStateMessageDTO;
import com.treasure.mall.biz.remote.live.dto.ShareSuccessMessageDTO;
import com.treasure.mall.biz.remote.live.enums.MessageType;
import com.treasure.mall.biz.service.core.BaseConfigService;
import com.treasure.mall.biz.service.core.live.*;
import com.treasure.mall.biz.service.core.user.UserService;
import com.treasure.mall.biz.vo.PageDataVO;
import com.treasure.mall.biz.vo.live.LiveRoomVideoVO;
import com.treasure.mall.zbq.common.annotation.CurrentUser;
import com.treasure.mall.zbq.common.base.ApiResult;
import com.treasure.mall.zbq.common.base.ResultCodeEnum;
import com.treasure.mall.zbq.common.exception.ApiException;
import com.treasure.mall.zbq.common.ienum.live.LiveEnums;
import com.treasure.mall.zbqmallapi.annotation.AccessControl;
import com.treasure.mall.zbqmallapi.vo.live.LiveRoomVO;
import com.treasure.mall.zbqmallapi.vo.live.LiveSessionVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * 直播相关接口
 * +---------------------------------------------------------
 * * @package: com.treasure.mall.zbqmallapi.controller
 * * @author: guomw (guomwchen@foxmail.com)
 * * @date: 2020/6/12 09:45
 * * @since: V1.0.0
 * +---------------------------------------------------------
 * 注意：本内容仅限于内部传阅，禁止外泄以及用于其他的商业目的
 * Copyright 2019-2022 com.treasure.mall.zbqmallapi.controller Inc. All rights reserved.
 */
@Slf4j
@RestController
@Api(tags = "直播模块")
public class LiveRoomController {
    @Resource
    private LiveRoomService liveRoomService;
    @Resource
    private LiveRoomFanService liveRoomFanService;
    @Resource
    private TencentIMService imService;
    @Resource
    private TencentLiveService tencentLiveService;
    @Resource
    private LiveSessionService liveSessionService;
    @Autowired
    private LiveHistoryMessageService liveHistoryMessageService;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;
    @Autowired
    private UserService userService;
    @Autowired
    private LiveRobotService liveRobotService;

    @Autowired
    private LiveDirectorService liveDirectorService;
    @Autowired
    private LiveRoomVideoService liveRoomVideoService;
    @Autowired
    private LiveRoomUserService liveRoomUserService;
    @Autowired
    private BaseConfigService baseConfigService;
    @Autowired
    private LiveRoomImagesService liveRoomImagesService;

    /**
     * @param page
     * @param size
     * @return
     */
    @ApiOperation("直播列表")
    @GetMapping("/room/list")
    public ApiResult liveRoomList(
            LiveRoomSearchCondition condition,
            @RequestParam(required = false, defaultValue = "1") Integer page,
            @RequestParam(required = false, defaultValue = "20") Integer size
    ) {
        if (condition.getPlatform() == null && (condition.getRecommendFlag() == null || !condition.getRecommendFlag())) {
            condition.setPlatform(true);
        }
        condition.setAuditStatus(LiveEnums.AuditStatus.SUCCESS.getCode());
        condition.setEnabled(true);
        Page<LiveRoomPO> pageData = liveRoomService.findAll(page, size, condition);
        List<LiveRoomVO> liveRoomVOS = new ArrayList<>();
        pageData.getContent().forEach(p -> {
            //如果获取推荐直播间，过滤掉非直播中的直播间
            if (condition.getRecommendFlag() != null && condition.getRecommendFlag()) {
                if (p.isPlaying()) {
                    liveRoomVOS.add(new LiveRoomVO(p, liveSessionService));
                }
            } else {
                liveRoomVOS.add(new LiveRoomVO(p, liveSessionService));
            }
        });
        return ApiResult.ok(getLiveRoomPage(pageData, size, page, liveRoomVOS));
    }

    /**
     * 检查是否存在授权直播间
     *
     * @param user
     * @return
     */
    @GetMapping("/room/check/auth")
    @AccessControl(needLogin = true)
    public ApiResult checkIsAuthLive(@CurrentUser UserPO user) {
        return ApiResult.ok(liveDirectorService.checkIsAuthLive(user.getUserId()));
    }

    /**
     * 检查是否直播中
     *
     * @param id 直播间ID
     * @return
     */
    @AccessControl(needLogin = true)
    @GetMapping("/room/check/playing/{id}")
    public ApiResult checkIsPlaying(@PathVariable("id") Long id) {
        LiveRoomPO roomPO = liveRoomService.findById(id);
        if (roomPO != null && roomPO.isPlaying()) {
            return ApiResult.error(ResultCodeEnum.LIVE_FAILED, "当前直播间已开播");
        }
        return ApiResult.ok(true);
    }


    /**
     * 获取我授权的直播间
     *
     * @param user
     * @return
     */
    @GetMapping("/room/list/auth/my")
    @AccessControl(needLogin = true)
    public ApiResult myAuthLiveRoomList(@CurrentUser UserPO user) {
        List<LiveRoomVO> liveRoomVOS = new ArrayList<>();
        List<LiveRoomPO> roomPOS = liveRoomService.getUserAuthRoomList(user.getUserId());
        if (roomPOS != null) {
            roomPOS.forEach(p -> {
                liveRoomVOS.add(new LiveRoomVO(p, liveSessionService));
            });
        }
        return ApiResult.ok(liveRoomVOS);
    }


    @ApiOperation("我关注的直播列表，必须要登录")
    @GetMapping("/room/list/my")
    @AccessControl(needLogin = true)
    public ApiResult myLiveRoom(@CurrentUser UserPO user,
                                @RequestParam(required = false, defaultValue = "1") Integer page,
                                @RequestParam(required = false, defaultValue = "20") Integer size) {
        LiveRoomFanCondition condition = new LiveRoomFanCondition();
        condition.setUserId(user.getUserId());
        Page<LiveRoomFanPO> pageData = liveRoomFanService.findAll(page, size, condition);
        List<LiveRoomVO> liveRoomVOS = new ArrayList<>();
        pageData.getContent().forEach(p -> liveRoomVOS.add(new LiveRoomVO(p.getRoom(), liveSessionService)));
        return ApiResult.ok(getLiveRoomPage(pageData, size, page, liveRoomVOS));
    }

    private PageDataVO<LiveRoomVO> getLiveRoomPage(Page pageData, int size, int page, List<LiveRoomVO> liveRoomVOS) {
        PageDataVO<LiveRoomVO> pageDataVO = new PageDataVO<>();
        pageDataVO.setPageSize(size);
        pageDataVO.setPageIndex(page);
        pageDataVO.setPageCount(pageData.getTotalPages());
        pageDataVO.setTotal(pageData.getTotalElements());
        //排序
        List<LiveRoomVO> playingList = new ArrayList<>();
        List<LiveRoomVO> otherList = new ArrayList<>();
        for (LiveRoomVO liveRoomVO : liveRoomVOS) {
            if (liveRoomVO.isPlaying()) {
                playingList.add(liveRoomVO);
            } else {
                otherList.add(liveRoomVO);
            }
        }
        playingList.addAll(otherList);

        pageDataVO.setRows(playingList);
        return pageDataVO;
    }


    /**
     * 获取直播间群组ID
     *
     * @param id
     * @return
     */
    @ApiOperation("获取直播间群组ID")
    @GetMapping("/liveRooms/{id}/im")
    public ApiResult im(@PathVariable("id") Long id) {
        LiveRoomPO room = liveRoomService.findById(id);
        return ApiResult.ok(imService.clientRoom(room));
    }

    /**
     * 获取直播间信息
     *
     * @param id 直播间ID
     * @return
     */
    @ApiOperation("获取直播间信息")
    @GetMapping("/liveRooms/{id}/info")
    public ApiResult getRoomInfo(@PathVariable("id") Long id) {
        LiveRoomPO room = liveRoomService.findById(id);
        LiveRoomVO liveRoomVO = new LiveRoomVO(room, liveSessionService);
        liveRoomVO.setNotice(baseConfigService.getZbConfig().getLiveNotice());
        return ApiResult.ok(liveRoomVO);
    }

    /**
     * 获取直播间信息
     *
     * @param user
     * @return
     */
    @ApiOperation("获取我的直播间信息")
    @GetMapping("/room/info/my")
    @AccessControl(needLogin = true)
    public ApiResult getMyLiveRoomInfo(@CurrentUser UserPO user) {
        if (user.getMerchantId() != null && user.getMerchantId() > 0) {
            LiveRoomPO room = liveRoomService.findByMerchant(user.getMerchantId());
            if (room == null) {
                return ApiResult.error(ResultCodeEnum.NOT_AUTHORITY);
            }
            LiveRoomVO liveRoomVO = new LiveRoomVO(room, liveSessionService);
            return ApiResult.ok(liveRoomVO);
        }
        return ApiResult.error(ResultCodeEnum.NOT_AUTHORITY);

    }

    /**
     * 获取店铺首页直播间
     *
     * @param merchantId
     * @return
     */
    @ApiOperation("获取店铺首页直播间")
    @GetMapping("/room/info/{merchantId}")
    public ApiResult getShopLiveRoomInfo(@PathVariable("merchantId") Long merchantId) {
        LiveRoomPO room = liveRoomService.findByMerchant(merchantId);
        if (room == null) {
            return ApiResult.error("暂无直播");
        }
        LiveRoomVO liveRoomVO = new LiveRoomVO(room, liveSessionService);
        return ApiResult.ok(liveRoomVO);
    }


    /**
     * 获取推流地址
     *
     * @param user
     * @param id   直播间ID
     * @return
     * @throws TencentCloudSDKException
     * @throws NoSuchAlgorithmException
     */
    @ApiOperation("获取推流地址")
    @GetMapping("/liveRooms/{id}/push")
    @Transactional(readOnly = true)
    @AccessControl(needLogin = true)
    public ApiResult pushUrl(@CurrentUser UserPO user, @PathVariable("id") Long id) throws TencentCloudSDKException, NoSuchAlgorithmException {
        LiveRoomPO room = getRoomWithAccessCheck(user, id);
        return ApiResult.ok(tencentLiveService.getPushUrl(room));
    }


    /**
     * 开始直播，首先要检查响应的状态，如果当时已经开启直播 而且直播者非本人 则应该拒绝，反之则应该继续其直播
     *
     * @param user
     * @param id   直播间ID
     * @return
     */
    @ApiOperation("开始直播")
    @PutMapping("/liveRooms/{id}/start")
    @AccessControl(needLogin = true)
    public ApiResult start(@CurrentUser UserPO user, @PathVariable("id") Long id) {
        LiveRoomPO roomPO = liveRoomService.findById(id);
        if (roomPO != null && roomPO.isPlaying()) {
            return ApiResult.error(ResultCodeEnum.LIVE_FAILED, "当前直播间已开播");
        }
        if (roomPO == null) {
            return ApiResult.error(ResultCodeEnum.LIVE_FAILED);
        }

        LiveSessionPO sessionPO = liveSessionService.start(id, roomPO.getData().getTitle(), user.getUserId());

        LiveRoomVO liveRoomVO = new LiveRoomVO(roomPO, null);
        LiveSessionVO vo = new LiveSessionVO(sessionPO, liveSessionService);
        liveRoomVO.setCurrentSession(vo);

        //私域直播，开播后，5分钟后自动开启机器人
//        if (!roomPO.isPlatform()) {
//            taskScheduler.schedule(() -> liveRobotService.startAutoJoin(id), Jsr310Utils.DateTime.toDate(LocalDateTime.now().plusMinutes(10)));
//        }
        // 发送开始直播消息
        threadPoolTaskExecutor.execute(() -> imService.sendLiveStateChangeMessage(LiveStateMessageDTO.builder()
                .messageType(MessageType.startPush)
                .roomId(roomPO.getId())
                .sessionId(sessionPO.getId())
                .playUrl(sessionPO.getPlayUrl())
                .build()
        ));
        return ApiResult.ok(liveRoomVO);
    }

    /**
     * 停止当前直播
     *
     * @param user
     * @param id
     */
    @ApiOperation("停止当前直播")
    @PutMapping("/liveRooms/{id}/stop")
    @AccessControl(needLogin = true)
    public ApiResult stop(@CurrentUser UserPO user, @PathVariable("id") Long id) {
        LiveRoomPO roomPO = liveRoomService.findById(id);
        getRoomWithAccessCheck(user, id);
        if (roomPO != null) {
            // 发送停止直播消息
            imService.sendLiveStateChangeMessage(LiveStateMessageDTO.builder()
                    .messageType(MessageType.stopPush)
                    .sessionId(roomPO.getSessionId())
                    .roomId(id)
                    .build()
            );
            liveSessionService.stop(roomPO.getSessionId());

            liveRoomVideoService.resetPushed(roomPO.getId());
            liveRoomImagesService.resetPushed(roomPO.getId());
        }

        return ApiResult.ok();
    }

    /**
     * 停止当前直播，并且中断数据流
     *
     * @param user
     * @param id   直播间ID
     */
    @ApiOperation("关闭当前直播")
    @PutMapping("/liveRooms/{id}/close")
    @AccessControl(needLogin = true)
    public ApiResult close(@CurrentUser UserPO user, @PathVariable("id") Long id) {
        getRoomWithAccessCheck(user, id);
        LiveRoomPO roomPO = liveRoomService.findById(id);
        if (roomPO != null) {
            // 发送停止直播消息
            imService.sendLiveStateChangeMessage(LiveStateMessageDTO.builder()
                    .messageType(MessageType.stopPush)
                    .sessionId(roomPO.getSessionId())
                    .roomId(id)
                    .build()
            );
            liveSessionService.stop(roomPO.getSessionId());
            tencentLiveService.closeStream(roomPO);
            liveRoomVideoService.resetPushed(roomPO.getId());
            liveRoomImagesService.resetPushed(roomPO.getId());
        }
        return ApiResult.ok();
    }


    private LiveRoomPO getRoomWithAccessCheck(UserPO user, Long id) {
        LiveRoomPO room = liveRoomService.findById(id);
        if (!room.isEnabled()) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ERROR, "该直播间已经被被禁"));
        }
        try {

            //判断是不是主播，如果不是，则无权操作
            boolean flag = liveRoomService.checkDirectorUser(room.getId(), user.getUserId());
            if (!flag) {
                throw new ApiException(ApiResult.errorWith(ResultCodeEnum.NOT_AUTHORITY, "您不是主播，无权操作"));
            }

//            if (user.getMerchantId() == null || !user.getMerchantId().equals(room.getMerchant().getMerchantId())) {
//                throw new ApiException(ApiResult.error(ResultCodeEnum.NOT_AUTHORITY));
//            }
        } catch (IllegalStateException e) {
            throw new ApiException(ApiResult.error(ResultCodeEnum.NOT_AUTHORITY));
        }
        return room;
    }

    /**
     * 进入直播间
     *
     * @param userId    用户ID
     * @param sessionId 直播间场次ID
     * @param custom    发送自定义消息 1表示发送
     * @return
     */
    @ApiOperation("进入直播间")
    @GetMapping("/liveRooms/userEnter")
    public ApiResult userEnter(Long userId, String sessionId, @RequestParam(required = false, defaultValue = "0") Integer custom) {
        liveSessionService.userEnter(userId, sessionId);
        try {
            if (custom != null && custom == 1) {
                UserPO user = userService.findById(userId);
                if (user != null) {
                    // 发送加入直播间消息
                    imService.join(JoinMessageDTO.builder()
                            .name(user.getNickName())
                            .liveSessionId(sessionId)
                            .level(TencentTools.getMessageUserLevel(user.getValidPayOrderSum()))
                            .build()
                    );
                }
            }
        } catch (Exception ignored) {

        }

        return ApiResult.ok();
    }

    /**
     * 离开直播间
     *
     * @param userId    用户ID
     * @param sessionId 直播间场次ID
     * @return
     */
    @ApiOperation("离开直播间")
    @GetMapping("/liveRooms/userLeave")
    public ApiResult userLeave(Long userId, String sessionId) {
        liveSessionService.userLeave(userId, sessionId);
        return ApiResult.ok();
    }

    /**
     * 关注直播间
     *
     * @param user   用户ID
     * @param roomId 直播间ID
     * @return
     */
    @ApiOperation("关注直播间")
    @GetMapping("/liveRooms/follow")
    @AccessControl(needLogin = true)
    public ApiResult follows(@CurrentUser UserPO user, Long roomId) {
        boolean exist = liveRoomFanService.countByRoomIdAndUserId(roomId, user.getUserId());
        if (exist) {
            return ApiResult.ok();
        }
        liveRoomService.follow(roomId, user.getUserId());
        return ApiResult.ok();
    }

    /**
     * 取消关注直播间
     *
     * @param user   用户ID
     * @param roomId 直播间ID
     * @return
     */
    @ApiOperation("取消关注直播间")
    @GetMapping("/liveRooms/unfollow")
    @AccessControl(needLogin = true)
    public ApiResult unfollow(@CurrentUser UserPO user, Long roomId) {
        liveRoomService.unfollow(roomId, user.getUserId());
        return ApiResult.ok();
    }

    /**
     * 获取当前会话记录
     *
     * @param sessionId 场次ID
     * @return
     */
    @ApiOperation("获取当前会话记录")
    @GetMapping("/liveRooms/currentSession/{sessionId}")
    @AccessControl(needLogin = true)
    public ApiResult getLiveSession(@PathVariable("sessionId") String sessionId) {
        LiveSessionPO po = liveSessionService.findById(sessionId);
        LiveSessionVO vo = new LiveSessionVO(po, liveSessionService);
        LiveRoomPO roomPO = liveRoomService.findById(po.getLiveRoomPO().getId());
        LiveRoomVO roomVO = new LiveRoomVO(roomPO, null);
        roomVO.setCurrentSession(vo);
        return ApiResult.ok(roomVO);
    }

    /**
     * 某个用户是否已关注直播间
     *
     * @param userPO
     * @param roomId 直播间ID
     * @return
     */
    @ApiOperation("是否已关注直播间")
    @GetMapping("/live/isRoomFan")
    @AccessControl(needLogin = true)
    public ApiResult isRoomFan(@CurrentUser UserPO userPO, Long roomId) {
        boolean exist = liveRoomFanService.countByRoomIdAndUserId(roomId, userPO.getUserId());
        if (!exist) {
            return ApiResult.ok(false);
        }
        return ApiResult.ok(true);
    }

    /**
     * 添加直播间历史消息
     *
     * @param user
     * @param sessionId 当前直播间场次信息
     * @param dto
     * @return
     */
    @ApiOperation("添加直播间历史消息")
    @PostMapping("/live/history/add/{sessionId}")
    @AccessControl(needLogin = true)
    public ApiResult addHistoryMessage(@CurrentUser UserPO user, @PathVariable("sessionId") String sessionId, @RequestBody ChatMessageBodyDTO dto) {
        liveHistoryMessageService.save(sessionId, user, dto);
        return ApiResult.ok();
    }

    /**
     * 获取历史消息日志
     *
     * @param user      用户
     * @param sessionId 当前直播场次ID
     * @return
     */
    @ApiOperation("获取历史消息日志")
    @GetMapping("/live/history/message/{sessionId}")
    @AccessControl(needLogin = true)
    public ApiResult getLiveHistoryMessage(@CurrentUser UserPO user, @PathVariable("sessionId") String sessionId) {
        LiveHistoryMessageCondition condition = new LiveHistoryMessageCondition();
        condition.setSessionId(sessionId);
        Page<LiveHistoryMessagePO> page = liveHistoryMessageService.findAll(1, 20, condition);
        List<ChatMessageBodyDTO> list = new ArrayList<>();
        page.getContent().forEach(item -> {
            if (StringUtils.isNotBlank(item.getMessageBody())) {
                ChatMessageBodyDTO messageBody = JSON.parseObject(item.getMessageBody(), ChatMessageBodyDTO.class);
                list.add(messageBody);
            }
        });
        return ApiResult.ok(list);
    }

    @ApiOperation("发送分享成功消息")
    @GetMapping("/live/share/success/{id}")
    @AccessControl(needLogin = true)
    public ApiResult shareSuccess(@CurrentUser UserPO user, @PathVariable("id") Long roomId) {
        LiveRoomPO roomPO = liveRoomService.findById(roomId);
        if (roomPO != null) {
            // 发送分享成功消息
            imService.sendShareSuccess(ShareSuccessMessageDTO.builder()
                    .roomId(roomId)
                    .nick(user.getNickName())
                    .userId(user.getUserId())
                    .validPayOrderSum(user.getValidPayOrderSum())
                    .build());
        }
        return ApiResult.ok();
    }

    /**
     * 获取直播间视频数据
     *
     * @param roomId
     * @return
     */
    @ApiOperation("获取直播间播放视频")
    @GetMapping("/live/video/{roomId}")
    @AccessControl(needLogin = true)
    public ApiResult getLiveRoomVideo(@PathVariable("roomId") Long roomId) {
        LiveRoomVideoVO vo = liveRoomVideoService.findLastByRoomId(roomId);
        if (vo != null) {
            return ApiResult.ok(vo);
        }
        return ApiResult.error("无数据");
    }

    /**
     * 检查是否禁言
     *
     * @param sessionId 直接间场次ID
     * @return
     */
    @ApiOperation("检查是否禁言")
    @GetMapping("/live/check/forbid/{sessionId}")
    @AccessControl(needLogin = true)
    public ApiResult checkForbid(@CurrentUser UserPO user, @PathVariable("sessionId") String sessionId) {
        return ApiResult.ok(liveRoomUserService.checkForbid(user.getUserId(), sessionId));
    }

    /**
     * 用户禁言
     *
     * @param sessionId 直播间场次ID
     * @param userId    用户ID
     * @return
     */
    @ApiOperation("用户禁言")
    @PostMapping("/live/forbid/enable/{sessionId}")
    @AccessControl(needLogin = true)
    public ApiResult setForbid(@CurrentUser UserPO user, @PathVariable("sessionId") String sessionId, Long userId) {
        LiveSessionPO po = liveSessionService.findById(sessionId);
        if (po == null) {
            return ApiResult.error("禁言失败");
        }
        //判断是不是主播，如果不是，则无权操作
        boolean flag = liveRoomService.checkDirectorUser(po.getLiveRoomPO().getId(), user.getUserId());
        if (!flag) {
            return ApiResult.error(ResultCodeEnum.NOT_AUTHORITY, "禁言失败");
        }
        return ApiResult.ok(liveRoomUserService.setForbid(userId, sessionId, true));
    }
}

